`define VERSION 32'h20000014

module debugger_usb3 (
  input           clk,
  input           clk_50M,
  input           reset_n,

  input        USB3_UART_IN,
  output       USB3_RST_OUT,
  output       USB3_PCLK,
  inout [31:0] USB3_DQ,
  output [1:0] USB3_A,
  output       USB3_SLCS_N,
  output       USB3_SLWR_N,
  output       USB3_SLOE_N,
  output       USB3_SLRD_N,
  output       USB3_PKTEND_N,
  input        USB3_FLAGA,
  input        USB3_FLAGB,
  input        USB3_FLAGC,
  input        USB3_FLAGD,
  input        USB3_CMD_CLK,
  input        USB3_CMD_DAT_U2F,
  output       USB3_CMD_DAT_F2U,

  output reg read_req,
  output reg write_req,
  input read_ack,
  input write_ack,

  output [31:0] ddr2_addr_out,
  output [31:0] debug_address_out,
  input [127:0] readValue,
  output reg [63:0] writeValue,
  output reg debug_write_en,
  input flg,
  

  input ddr2_clk,
  input [1:0] ddr2_addr,// 11:2, 1:0
  input [63:0] local_rdata,
  input local_rdata_valid,
  output [63:0] local_wdata,
  
  output reg resetIndex,
  input [31:0] recorderIndex,
  output reg need_refresh,

  input dummy
);

assign debug_address_out = debug_address;
assign ddr2_addr_out = {debug_address[31:11], ddr2_addr_prefix, 2'b0};// 11:2, 1:0

altsource_probe  altsource_probe_component1 (
  .probe (debug_count),
  .source ()
);
defparam
  altsource_probe_component1.enable_metastability = "NO",
  altsource_probe_component1.instance_id = "DATA",
  altsource_probe_component1.probe_width = 32,
  altsource_probe_component1.sld_auto_instance_index = "YES",
  altsource_probe_component1.sld_instance_index = 1,
  altsource_probe_component1.source_initial_value = " 0",
  altsource_probe_component1.source_width = 32;

wire [31:0] ft_read_data;
assign USB3_DQ = ft_out_to_pc ? ft_read_data : 32'bz;
assign USB3_RST_OUT = rst_count[24] ? 1'bz : 1'b0;
assign USB3_PCLK = usb_clk;
assign USB3_A = ft_out_to_pc ? 2'b01 : 2'b11;
assign USB3_SLCS_N = cs ? 1'b0 : 1'b1;
assign USB3_SLWR_N = wr ? 1'b0 : 1'b1;
assign USB3_SLOE_N = ft_out_to_pc ? 1'b1 : 1'b0;
assign USB3_SLRD_N = rd ? 1'b0 : 1'b1;
assign USB3_PKTEND_N = packet_end ?  1'b0 : 1'b1;
assign USB3_CMD_DAT_F2U = debug_data_out_buff[31];


reg [24:0] rst_count;
always @(posedge clk_50M or negedge reset_n) begin
  if (!reset_n) begin
    rst_count <= 0;
  end else begin
    if(!rst_count[24])begin
      rst_count <= rst_count + 1'b1;
    end
  end
end


//wire usb_clk = clk_50M;
pll_usb pll_usb_inst(
  .areset  (~reset_n),
  .inclk0             (clk_50M),
  .c0                 (usb_clk),
  .c1                 (usb_clk_out),
  .locked             ()
);


localparam TRANS_IDLE          = 0;
localparam TRANS_RD_DELAY1     = 1;
localparam TRANS_RD_DELAY2     = 2;
localparam TRANS_RD_DELAY3     = 3;
localparam TRANS_RD_DELAY4     = 8;
localparam TRANS_RD            = 4;
localparam TRANS_RD_END        = 5;
localparam TRANS_WR            = 6;
localparam TRANS_WR_END        = 7;

reg   [3:0] ft_status;
reg [11:0] ft_addr;
reg [3:0] trans_status;
reg ft_write;
reg  [31:0] ft_write_data;
reg wr;
reg packet_end;
reg rd;
wire cs = 1;
reg transfer_req_buff;
reg transfer_ack;
reg [4:0] delay_count;

reg [4:0] delay_cnt;
localparam usb3_cnt_max = 4095;
reg [11:0] usb3_cnt;
reg ft_out_to_pc;
reg ft_out_to_pc_buff;
reg reset_fifo_buff;

reg [31:0] debug_count;
always @(posedge usb_clk or negedge reset_n) begin
  if (!reset_n) begin
    rd <= 0;
    ft_write <= 0;
    transfer_req_buff <= 0;
    ft_out_to_pc_buff <= 0;
    transfer_ack <= 0;
    trans_status <= 0;
    ft_addr <= 0;
    packet_end <= 0;
    delay_count <= 0;
    ft_status <= 0;
    wr <= 0;
    usb3_cnt <= 0;
    debug_count <= 0;
  end else begin
    transfer_req_buff <= transfer_req;
    ft_out_to_pc_buff <= ft_out_to_pc;
    reset_fifo_buff <= reset_fifo;
    wr <= 0;
    ft_write <= 0;
    packet_end <= 0;
    if(transfer_req_buff)begin
      if(ft_out_to_pc_buff == 0)begin
        // from pc
        if(ft_write)begin
          ft_addr <= ft_addr + 1'b1;
        end
        case(ft_status)
        0: begin
          ft_status <= 6;
        end
        6: begin
          usb3_cnt <= 0;
          delay_cnt <= 0;
          if(USB3_FLAGC==0)begin // && fifo_write_ok
            ft_status <= 1;
          end
        end
        1: begin
          rd <= 1;
          ft_status <= 2;
        end
        2: begin
          ft_status <= 3;
        end
        3: begin
          ft_status <= 4;
        end
        4: begin
          ft_write <= 1;
          ft_write_data <= USB3_DQ;
          usb3_cnt <= usb3_cnt + 1'b1;
          if(usb3_cnt==usb3_cnt_max-2)begin
            rd <= 0;
          end
          if(usb3_cnt==usb3_cnt_max)begin
            ft_status <= 5;
          end
        end
        5: begin
          delay_cnt <= delay_cnt + 1'b1;
          if(delay_cnt == 20)begin
            ft_status <= 7;
          end
        end
        7: begin
        end
        endcase


      end else begin
        // to pc

        case(ft_status)
        0: begin
          ft_status <= 6;
        end
        6: begin
          usb3_cnt <= 0;
          delay_cnt <= 0;
          if(USB3_FLAGA==0)begin// && fifo_read_ok
            ft_status <= 1;
          end
        end
        1: begin
          ft_status <= 2;
        end
        2: begin
          ft_status <= 3;
        end
        3: begin
          ft_status <= 4;
        end
        4: begin
          wr <= 1;
          ft_addr <= ft_addr + 1'b1;
          usb3_cnt <= usb3_cnt + 1'b1;
          debug_count <= debug_count + 1'b1;
          if(usb3_cnt==usb3_cnt_max)begin
            packet_end <= 1;
            ft_status <= 5;
          end
        end
        5: begin
          delay_cnt <= delay_cnt + 1'b1;
          if(delay_cnt == 20)begin
            ft_status <= 7;
          end
        end
        7: begin
        end
        endcase


      end
    end

    
    if(reset_fifo_buff)begin
      ft_status <= 0;
      ft_addr <= 0;
      // wren_a <= 0;
      // transfer_req_buff <= 0;
      // transfer_ack <= 0;
      // trans_status <= 0;
      // usb3_addr <= 0;
      // packet_end <= 0;
      // delay_count <= 0;
    end
  end
end




  reg command_req;
  reg command_ack;
  reg debug_data_send;
  reg [31:0] debug_data_out;
  wire [31:0] data;
  wire [7:0] command;
  assign debug_regAddr = debug_address[5:0];




localparam S_IDLE     = 0;
localparam S_CMD_IN   = 1;
localparam S_CMD_OUT  = 2;
localparam S_CMD_OUT_1= 3;

  assign command = cmd_data[7:0];
  assign data = cmd_data[39:8];
  reg [39:0] cmd_data;
  reg [5:0] cnt_in;
  reg [3:0] status;
  reg [31:0] debug_data_out_buff;
  reg command_req_src;
  reg command_ack_buff;
  always @(posedge USB3_CMD_CLK or negedge reset_n) begin
    if (!reset_n) begin
    cnt_in <= 0;
    status <= 0;
    cmd_data <= 0;
    debug_data_out_buff <= 0;
    command_req_src <= 0;
    command_ack_buff <= 0;
    end else begin
    command_ack_buff <= command_ack;
    case(status)
    S_IDLE:begin
      debug_data_out_buff <= 0;
      if(USB3_CMD_DAT_U2F)begin
        cnt_in <= 0;
        status <= S_CMD_IN;
      end
      if(command_ack_buff)begin
        cnt_in <= 0;
        status <= S_CMD_OUT_1;
        debug_data_out_buff <= {1'b1,31'b0};
      end
    end
    S_CMD_IN:begin
      cnt_in <= cnt_in + 1'b1;
      if(cnt_in==40)begin
        status <= S_IDLE;
        command_req_src <= 1;
      end else begin
        cmd_data <= {USB3_CMD_DAT_U2F,cmd_data[39:1]};
      end
    end
    S_CMD_OUT_1:begin
      debug_data_out_buff <= debug_data_out;
      status <= S_CMD_OUT;
    end
    S_CMD_OUT:begin
      cnt_in <= cnt_in + 1'b1;
      debug_data_out_buff <= {debug_data_out_buff[30:0], 1'b1};
      if(cnt_in==31)begin
        status <= S_IDLE;
      end
    end
    endcase
    
    if(command_ack_buff)begin
      command_req_src <= 0;
    end
    
    end
  end

  always @(posedge clk_50M or negedge reset_n) begin
    if (!reset_n) begin
    command_req <= 0;
    end else begin
    command_req <= command_req_src;
    end
  end




wire [31:0] debug_write_q;

//assign buffer_q = (~fifo_addr[0]) ? q_ah : q_al;// 1 cycle delay, flip is need
  altsyncram fifoBuff (

    .clock0 (usb_clk),
    .address_a (ft_addr),
    .data_a (ft_write_data),
    .wren_a (ft_write),
    .q_a (ft_read_data),
    
    .clock1 (clk_50M),
    .address_b (debug_address),
    .data_b (debug_writedata),
    .wren_b (debug_write_en),
    //.byteena_b (),
    .q_b (debug_write_q),
    
    // .clock1 (ddr2_clk),
    // .address_b ({ddr2_addr_prefix, ddr2_addr}),// 10:2, 1:0
    // .data_b (local_rdata),
    // .wren_b (local_rdata_valid),
    // .byteena_b (8'b11111111),
    // .q_b (local_wdata),
    


    .aclr0 (1'b0),
    .aclr1 (1'b0),
    .addressstall_a (1'b0),
    .addressstall_b (1'b0),
    .clocken0 (1'b1),
    .clocken1 (1'b1),
    .clocken2 (1'b1),
    .clocken3 (1'b1),
    .eccstatus (),
    .rden_a (1'b1),
    .rden_b (1'b1));
  defparam
    fifoBuff.address_reg_b = "CLOCK1",
    fifoBuff.clock_enable_input_a = "BYPASS",
    fifoBuff.clock_enable_input_b = "BYPASS",
    fifoBuff.clock_enable_output_a = "BYPASS",
    fifoBuff.clock_enable_output_b = "BYPASS",
    fifoBuff.indata_reg_b = "CLOCK1",
    fifoBuff.intended_device_family = "Cyclone IV E",
    fifoBuff.lpm_type = "altsyncram",
    fifoBuff.operation_mode = "BIDIR_DUAL_PORT",
    fifoBuff.outdata_aclr_a = "NONE",
    fifoBuff.outdata_aclr_b = "NONE",
    fifoBuff.outdata_reg_a = "UNREGISTERED",
    fifoBuff.outdata_reg_b = "UNREGISTERED",
    fifoBuff.power_up_uninitialized = "FALSE",
    fifoBuff.ram_block_type = "M9K",
    fifoBuff.read_during_write_mode_port_a = "NEW_DATA_NO_NBE_READ",
    fifoBuff.read_during_write_mode_port_b = "NEW_DATA_NO_NBE_READ",
    fifoBuff.numwords_a = 4096,//16k byte
    fifoBuff.numwords_b = 4096,//16k byte
    fifoBuff.widthad_a = 12,
    fifoBuff.widthad_b = 12,
    fifoBuff.width_a = 32,
    fifoBuff.width_b = 32,
    fifoBuff.width_byteena_a = 1,
    fifoBuff.width_byteena_b = 1,
    fifoBuff.byte_size = 8,
    fifoBuff.wrcontrol_wraddress_reg_b = "CLOCK1";














always @(*)begin
  case(command[5:0])

    8'h20: begin debug_data_out<=data; end //GET_CONST_ECHO
    8'h21: begin debug_data_out<=0; end //GET_CONST_ECHO
    8'h24: begin debug_data_out<=`VERSION; end //GET_CONST_ECHO
    8'h25: begin debug_data_out<=recorderIndex; end

    8'h26: begin debug_data_out<=debug_write_q[31:0]; end //GET_CONST_ECHO


    8'h2A: begin debug_data_out<={USB3_FLAGA, USB3_FLAGB, USB3_FLAGC, USB3_FLAGD}; end //GET_CONST_ECHO
    8'h2B: begin debug_data_out<=status; end //GET_CONST_ECHO
  
    8'h2C: begin debug_data_out<=readValue[31:0]; end //GET_CONST_ECHO
    8'h2D: begin debug_data_out<=readValue[63:32]; end //GET_CONST_ECHO
    8'h2E: begin debug_data_out<=readValue[95:64]; end //GET_CONST_ECHO
    8'h2F: begin debug_data_out<=readValue[127:96]; end //GET_CONST_ECHO
  
    default: begin
      debug_data_out<=0;
    end
  endcase
end
  
  reg [3:0] usbddr_status;

  //reg debug_write_en;
  reg read_ack_buff;
  reg write_ack_buff;
  reg [8:0] ddr2_addr_prefix;// 11:2, 1:0
  reg [8:0] ddr2_trans_len;
  reg rd_req;
  reg vga_control_by_debugger_reg2;
  reg transfer_req;
  reg transfer_ack_buff;
  reg [31:0] debug_address;
  reg [31:0] debug_writedata;
  reg [31:0] debug_length;
  reg [0:0] debug_mem_step;
  reg [9:0] vga_count;
  reg spirom_debug_read_ack_buff;
  reg spirom_debug_write_ack_buff;
  reg mem_ack_buff;
  reg reset_fifo;
  reg usb2ddr_req;
  reg ddr2usb_req;
  reg usb2ddr_ack;
  reg ddr2usb_ack;

  always @(posedge clk_50M or negedge reset_n) begin
    if (!reset_n) begin
      reset_fifo <= 0;
      transfer_req <= 0;

      ft_out_to_pc <= 0;
      debug_mem_step <= 0;
      
      usb2ddr_req <= 0;
      ddr2usb_req <= 0;
      usb2ddr_ack <= 0;
      ddr2usb_ack <= 0;

      debug_address<=0;
      debug_writedata<=0;
      
      usbddr_status <= 0;

      read_ack_buff<=0;
      write_ack_buff<=0;

      command_ack <= 0;
      debug_data_send<=0;
      mem_ack_buff <= 0;

      ddr2_trans_len <= 0;
      debug_write_en <= 0;
      transfer_ack_buff <= 0;

    end else begin

      debug_data_send <= 0;
      transfer_ack_buff <= transfer_ack;
      read_ack_buff <= read_ack;
      write_ack_buff <= write_ack;

      debug_write_en <= 0;
      if(debug_write_en)begin
        debug_address <= debug_address + 1'b1;
      end

      usb2ddr_req <= 0;
      ddr2usb_req <= 0;
      usb2ddr_ack <= 0;
      ddr2usb_ack <= 0;

      case(usbddr_status)
      0: begin
        ddr2_addr_prefix <= 0;
        if(usb2ddr_req)begin // write
          usbddr_status <= 1;
        end
        if(ddr2usb_req)begin// read
          usbddr_status <= 5;
        end
      end
      1: begin
        write_req <= 1;
        usbddr_status <= 2;
      end
      2: begin
        if(write_ack_buff)begin
          write_req <= 0;
          usbddr_status <= 3;
        end
      end
      3: begin
        if(!write_ack_buff)begin
          ddr2_addr_prefix <= ddr2_addr_prefix + 1'b1;
          if(ddr2_addr_prefix==ddr2_trans_len)begin
            usbddr_status <= 4;
            usb2ddr_ack <= 1;
          end else begin
            usbddr_status <= 1;
          end
        end
      end
      4: begin
        if(usb2ddr_req==0 && ddr2usb_req==0)begin
          usbddr_status <= 0;
        end
      end
      5: begin
        read_req <= 1;
        usbddr_status <= 6;
      end
      6: begin
        if(read_ack_buff)begin
          read_req <= 0;
          usbddr_status <= 7;
        end
      end
      7: begin
        if(!read_ack_buff)begin
          ddr2_addr_prefix <= ddr2_addr_prefix + 1'b1;
          if(ddr2_addr_prefix==ddr2_trans_len)begin
            usbddr_status <= 4;
            ddr2usb_ack <= 1;
          end else begin
            usbddr_status <= 5;
          end
        end
      end
      endcase


      if(command_req && !command_ack)begin
        case(command[6:0])
        
        8'h01 : begin debug_data_send<=1; ft_out_to_pc <= data[0]; command_ack<=1; end//SET_DEBUG_ADDRESS
        8'h02 : begin debug_data_send<=1; reset_fifo<=data[0]; command_ack<=1; end //HALT_DEBUG
        8'h04 : begin debug_data_send<=1; transfer_req<=data[0]; command_ack<=1; end //HALT_DEBUG
        // 8'h08 : begin 
        //   transfer_req <= 1;
        //   if(transfer_ack_buff)begin
        //     debug_data_send<=1; command_ack<=1; 
        //   end

        // end//SET_DEBUG_ADDRESS
        8'h06 : begin debug_data_send<=1; debug_writedata <= data; command_ack<=1; debug_write_en <= 1; end//SET_DEBUG_ADDRESS
    
    
    
    
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        8'h10 : begin debug_data_send<=1; ddr2_trans_len <= data; command_ack<=1; end
        8'h11 : begin debug_data_send<=1; resetIndex <= data[0]; command_ack<=1; end

        8'h12 : begin debug_data_send<=1; writeValue[31:0] <= data; command_ack<=1; end
        8'h13 : begin debug_data_send<=1; writeValue[63:32] <= data; command_ack<=1; end

        8'h15 : begin debug_data_send<=1; need_refresh <= data[0]; command_ack<=1; end

        
        
        8'h16 : begin debug_data_send<=1; debug_address <= data; command_ack<=1; end//SET_DEBUG_ADDRESS
        8'h17 : begin debug_data_send<=1; debug_writedata <= data; command_ack<=1; end//SET_DEBUG_WRITEDATA


        8'h20,8'h21,8'h22,8'h23,8'h24,8'h25,8'h26,8'h27,
        8'h28,8'h29,8'h2A,8'h2B,8'h2C,8'h2D,8'h2E,8'h2F : begin
      //if(command[5:0] == 8'h26)begin
          //  debug_address <= data;
      //end

          debug_data_send<=1; command_ack<=1;
      
        end //GET_CONST_ECHO


        8'h40 : begin
          usb2ddr_req <= 1;
          if(usb2ddr_ack)begin
            debug_data_send<=1; command_ack<=1; 
          end
        end


        8'h41 : begin
          ddr2usb_req <= 1;
          if(ddr2usb_ack)begin
            debug_data_send<=1; command_ack<=1; 
          end
        end

      
        default: begin
          command_ack<=1;
        end

        
        endcase

      end

      if(!command_req && command_ack)begin
        command_ack<=0;
      end
      
      if(reset_fifo)begin
        debug_mem_step <= 0;
      end
    end
  end



endmodule
